/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtClientRequest.h"
#include "WtTable.h"
#include "WtTableUtil.h"
#include "WtUpload.h"
#include "WtUtil.h"
#include "WtMultiTable.h"

/* Create ApacheRequest */

void WtInitApacheWeb(WtContext *w)
{
  if (!w->web->clientRequest.apWeb) {
    w->web->clientRequest.apWeb = ApacheRequest_new(w->web->apReq);
  }
}

/* Read the request data */

int WtReadRequest(WtContext *w, Tcl_Interp *interp)
{
  int ok = 1;
  table *tbl;
  ApacheUpload *upload;
  Tcl_Obj *uploadObj;

  if (w->web->clientRequest.readRequestStatus == 1) {
    return 1;
  } else if (w->web->clientRequest.readRequestStatus == 2) {
    Tcl_AppendResult(interp, "Failed to read request.", NULL);
    return 0;
  }

  WtInitApacheWeb(w);

  if (!WtParseRequest(w, interp)) {
    ok = 0;
  }

  if (ok) {
    tbl = ApacheRequest_query_params(w->web->clientRequest.apWeb,
      w->web->apReq->pool);
    if (!WtOwnTable(&w->web->clientRequest.queryParams, interp)) {
      ok = 0;
    } else if (!WtUpdateTableFromAp(w->web->clientRequest.queryParams,
        tbl, interp)) {
      ok = 0;
    }
  }

  if (ok) {
    tbl = ApacheRequest_post_params(w->web->clientRequest.apWeb,
      w->web->apReq->pool);
    if (!WtOwnTable(&w->web->clientRequest.postParams, interp)) {
      ok = 0;
    } else if (!WtUpdateTableFromAp(w->web->clientRequest.postParams,
        tbl, interp)) {
      ok = 0;
    }
  }

  if (ok) {
    if (!WtReadRequestCookies(w, interp)) {
      ok = 0;
    }
  }

  if (ok) {
    upload = ApacheRequest_upload(w->web->clientRequest.apWeb);
  
    for (; upload; upload = upload->next) {
      WtLog(HERE, APLOG_DEBUG | APLOG_NOERRNO, w,
        "Wtcl: Received uploaded file "
        "(upload = 0x%x, name = \"%s\", filename = \"%s\", "
        "content type = \"%s\", size = %d).",
        upload, WtSafeStr(upload->name),
        WtSafeStr(upload->filename),
        WtSafeStr(ApacheUpload_type(upload)),
        ApacheUpload_size(upload),
        NULL);

      if (!(uploadObj = WtNewUploadObj(upload, interp))) {
        ok = 0;
        break;
      }

      if (!WtOwnTable(&w->web->clientRequest.uploads, interp)) {
        ok = 0;
        break;
      }

      if (!WtTableAppendObjToList(w->web->clientRequest.uploads,
          WtSafeStr(upload->name), uploadObj, interp)) {
        ok = 0;
        break;
      }

      WtTableRemoveStr(w->web->clientRequest.postParams,
        WtSafeStr(upload->name));
    }
  }

  if (ok) {
    if (!WtOwnTable(&w->web->clientRequest.params, interp)) {
      ok = 0;
    } else if (!WtUpdateTable(w->web->clientRequest.params,
        w->web->clientRequest.queryParams)) {
      ok = 0;
    } else if (!WtUpdateTable(w->web->clientRequest.params,
        w->web->clientRequest.postParams)) {
      ok = 0;
    }
  }

  if (!ok) {
    if (WtOwnTable(&w->web->clientRequest.queryParams, interp)) {
      WtClearTable(w->web->clientRequest.queryParams);
    }

    if (WtOwnTable(&w->web->clientRequest.postParams, interp)) {
      WtClearTable(w->web->clientRequest.postParams);
    }

    if (WtOwnTable(&w->web->clientRequest.params, interp)) {
      WtClearTable(w->web->clientRequest.params);
    }

    if (WtOwnTable(&w->web->clientRequest.uploads, interp)) {
      WtClearTable(w->web->clientRequest.uploads);
    }

    w->web->clientRequest.readRequestStatus = 2;
  } else {
    w->web->clientRequest.readRequestStatus = 1;
  }

  return ok;
}

int WtParseRequest(WtContext *w, Tcl_Interp *interp)
{
  const char *errMsg;

  WtInitApacheWeb(w);

  if (w->web->clientRequest.parseStatus == 1) {
    return 1;
  } else if (w->web->clientRequest.parseStatus == 2) {
    Tcl_AppendResult(interp, "Failed to parse request.", NULL);
    return 0;
  }

  if (ApacheRequest_parse(w->web->clientRequest.apWeb) != OK) {
    w->web->clientRequest.parseStatus = 2;
    if (interp) {
      errMsg = ap_table_get(w->web->apReq->notes, "error-notes");
      if (errMsg && *errMsg) {
        Tcl_AppendResult(interp, "Failed to read request data: ",
          errMsg, NULL);
      } else {
        Tcl_AppendResult(interp, "Failed to read request data.",
          NULL);
      }
    }
  } else {
    w->web->clientRequest.parseStatus = 1;
  }

  return w->web->clientRequest.parseStatus == 1;
}

/* Read the request cookies */

int WtReadRequestCookies(WtContext *w, Tcl_Interp *interp)
{
  int ok = 1, count, i, nvals, j;
  ApacheCookie *cookie;
  char *name, *val;

  if (w->web->clientRequest.readCookiesStatus != 0) {
    return w->web->clientRequest.readCookiesStatus == 1;
  }

  WtInitApacheWeb(w);
  w->web->clientRequest.apCookies = ApacheCookie_parse(w->web->apReq, NULL);
  if (!w->web->clientRequest.apCookies) {
    ok = 0;
  }

  if (ok) {
    count = ApacheCookieJarItems(w->web->clientRequest.apCookies);
    for (i = 0; i < count && ok; i++) {
      cookie = ApacheCookieJarFetch(w->web->clientRequest.apCookies, i);
      if (cookie) {
        name = cookie->name ? cookie->name : "";
        nvals = ApacheCookieItems(cookie);
        for (j = 0; j < nvals; j++) {
          val = ApacheCookieFetch(cookie, j);
          if (!val) {
            val = "";
          }

          if (!WtOwnTable(&w->web->clientRequest.cookies, interp)) {
            ok = 0;
            break;
          }

          if (!WtTableAppendStrToList(w->web->clientRequest.cookies,
              name, val, interp)) {
            ok = 0;
            break;
          }
        }
      }
    }
  }

  if (!ok) {
    if (WtOwnTable(&w->web->clientRequest.cookies, interp)) {
      WtClearTable(w->web->clientRequest.cookies);
    }
    w->web->clientRequest.readCookiesStatus = 2;
  } else {
    w->web->clientRequest.readCookiesStatus = 1;
  }

  return ok;
}

/* queryParams command */

int WtQueryParamsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = WtGetAssocContext(interp);

  if (!WtReadRequest(w, interp)) {
    return TCL_ERROR;
  }

  WtMultiTableMethodHelper(clientData, interp, objc, objv, &ret,
    &w->web->clientRequest.queryParams, &WtTableType, 0, 1);

  return ret;
}

/* postParams command */

int WtPostParamsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = WtGetAssocContext(interp);

  if (!WtReadRequest(w, interp)) {
    return TCL_ERROR;
  }

  WtMultiTableMethodHelper(clientData, interp, objc, objv, &ret,
    &w->web->clientRequest.postParams, &WtTableType, 0, 1);

  return ret;
}

/* params command */

int WtParamsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = WtGetAssocContext(interp);

  if (!WtReadRequest(w, interp)) {
    return TCL_ERROR;
  }

  /* XXX: Add separate init for combined params set */

  WtMultiTableMethodHelper(clientData, interp, objc, objv, &ret,
    &w->web->clientRequest.params, &WtTableType, 0, 1);

  return ret;
}

/* requestCookies command */

int WtRequestCookiesCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = WtGetAssocContext(interp);

  if (!WtReadRequest(w, interp)) {
    return TCL_ERROR;
  }

  WtMultiTableMethodHelper(clientData, interp, objc, objv, &ret,
    &w->web->clientRequest.cookies, &WtTableType, 0, 1);

  return ret;
}

/* requestHeaders command */

int WtRequestHeadersCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, size;
  WtContext *w = WtGetAssocContext(interp);

  if (WtTableObjSize(w->web->clientRequest.headers, &size,
      &WtCaselessTableType, interp)) {
    if (size != 0) {
      ret = TCL_OK;
    } else if (WtOwnCaselessTable(&w->web->clientRequest.headers,
        interp)) {
      if (WtUpdateTableFromAp(w->web->clientRequest.headers,
          w->web->apReq->headers_in, interp)) {
        ret = TCL_OK;
      }
    }
  }

  if (ret == TCL_OK) {
    WtMultiTableMethodHelper(clientData, interp, objc,
      objv, &ret, &w->web->clientRequest.headers,
      &WtCaselessTableType, 0, 1);
  }

  return ret;
}

/* Initialize client request commands */

void WtInitClientRequestCommands(Tcl_Interp *interp)
{
  Tcl_CreateObjCommand(interp, "::wt::request::queryParams",
    WtQueryParamsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::request::postParams",
    WtPostParamsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::request::params",
    WtParamsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::request::cookies",
    WtRequestCookiesCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::request::requestHeaders",
    WtRequestHeadersCmd, NULL, NULL);
}
